/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

#define TRACE_LEVEL	2	// warning level


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "Filter.h"

// project's headers
#include "Assert.h"
#include "Reason.h"
#include "Rule.h"
#include "Trace.h"

// standard headers
#include <tchar.h>


/******************************************************************/
/* Internal constants                                             */
/******************************************************************/

// ... empty ...


/******************************************************************/
/* Internal data types                                            */
/******************************************************************/

typedef struct
{
  TCHAR		szProgram[MAX_PATH] ;
  RULE		*aRules[_REASON_COUNT] ;
  BOOL		bHookEnabled ;
  // !!!!!!!!!!!!!!!!!! it could be good to add a mutex
} FILTER ;



HFILTER Filter_Create (LPCTSTR szProgram) 
{
  FILTER * p = calloc (1,sizeof(FILTER)) ;

  TRACE ;

  if( szProgram )
    _tcscpy (p->szProgram, szProgram) ;
  else
    p->szProgram[0] = 0 ;
  p->bHookEnabled = TRUE ;
		      
  return p ;
}

VOID Filter_Destroy (HFILTER hFilter) 
{
  FILTER * p = hFilter ;
  UINT iReason ;

  TRACE ;
  
  for( iReason=0 ; iReason<_REASON_COUNT ; iReason++ )
    Filter_ResetRules (p, iReason) ;
	
  free (hFilter) ;
}

BOOL Filter_EnumRules (HFILTER hFilter, ENUMRULESCALLBACK pfnCallback, LPVOID pContext) 
{
  FILTER * p = hFilter ;
  UINT iReason ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pfnCallback!=NULL) ;
  
  for( iReason=0 ; iReason<_REASON_COUNT ; iReason++ )
    {
      TRACE ;

      RULE * pRule = p->aRules[iReason] ;

      while( pRule )
	{
	  TRACE ;

	  pfnCallback (pContext, pRule) ;
	  pRule = pRule->pNext ;
	}
    }

  return TRUE ;
}


LPCTSTR Filter_GetProgram (HFILTER hFilter) 
{
  FILTER * p = hFilter ;

  TRACE ;

  return p ? p->szProgram : NULL ;
}


VOID Filter_SetProgram (HFILTER hFilter, LPCTSTR szProgram) 
{
  FILTER * p = hFilter ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (szProgram!=NULL) ;

  // set process name
  _tcscpy (p->szProgram, szProgram) ;
}



BOOL Filter_ResetRules (HFILTER hFilter, UINT nReason) 
{
  FILTER * p = hFilter ;
  RULE * pRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (nReason>=0) ;
  ASSERT (nReason<_REASON_COUNT) ;

  while( (pRule=p->aRules[nReason]) )
    {
      p->aRules[nReason] = pRule->pNext ;
      
      free (pRule->pCondition) ;
      free (pRule) ;    
    }

  return TRUE ;
}


BOOL Filter_AddRules (HFILTER hFilter, RULE * pNewRules) 
{
  FILTER	*p = hFilter ;
  RULE		*pCurRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pNewRules!=NULL) ;
  ASSERT (pNewRules->pCondition!=NULL) ;
  ASSERT (pNewRules->pCondition->nReason>=0) ;
  ASSERT (pNewRules->pCondition->nReason<_REASON_COUNT) ;

  pCurRule = p->aRules[pNewRules->pCondition->nReason] ;
  
  if( ! pCurRule ) {
    p->aRules[pNewRules->pCondition->nReason] = pNewRules ;
    return TRUE ;
  }
  
  while( pCurRule->pNext ) pCurRule = pCurRule->pNext ;

  pCurRule->pNext = pNewRules ;

  return TRUE ;
}

BOOL Filter_AddRule (HFILTER hFilter, RULE * pNewRule) 
{
  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pNewRule!=NULL) ;
  
  pNewRule->pNext = NULL ;
  
  return Filter_AddRules (hFilter, pNewRule) ;
}


BOOL Filter_AddNewRule (HFILTER hFilter, WORD nReaction, WORD nVerbosity,
			WORD nOptions, LPCONDITION pCond) 
{
  RULE * pNewRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pCond!=NULL) ;
  ASSERT (pCond->nReason>=0) ;
  ASSERT (pCond->nReason<_REASON_COUNT) ;

  pNewRule = malloc (sizeof(RULE)) ;
  if( ! pNewRule ) return FALSE ;

  pNewRule->nReaction	= nReaction ;
  pNewRule->nVerbosity	= nVerbosity ;
  pNewRule->nOptions	= nOptions ;
  pNewRule->pCondition  = pCond ;
  pNewRule->pNext	= NULL ;
  
  return Filter_AddRules (hFilter, pNewRule) ;
}

BOOL	Filter_DeleteRule (HFILTER hFilter, RULE *pRuleToDelete) 
{
  FILTER	*p = hFilter ;
  RULE		*pRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pRuleToDelete!=NULL) ;
 
  pRule = p->aRules[pRuleToDelete->pCondition->nReason] ;

  // is it the first rule ?
  if( pRule==pRuleToDelete )
    {
      p->aRules[pRuleToDelete->pCondition->nReason] = pRule->pNext ;
      free (pRuleToDelete) ;
      return TRUE ;
    }

  // for each rule (begin)
  while( pRule )
    {
      // is it the next rule ?
      if( pRule->pNext == pRuleToDelete )
	{
	  pRule->pNext = pRule->pNext->pNext ;
	  free (pRuleToDelete) ;
	  return TRUE ;
	}

      // iterate list
      pRule = pRule->pNext ;
    }
  // for each rule (end)

  return FALSE ;
}


BOOL Filter_Concat (HFILTER hFilter, HFILTER hFilterToAdd) 
{
  FILTER	* p1 = (FILTER*) hFilter ;
  FILTER	* p2 = (FILTER*) hFilterToAdd ;
  int		i ;

  TRACE ;

  // verify params
  ASSERT  (hFilter!=NULL) ;
  ASSERT  (hFilterToAdd!=NULL) ;

  for( i=0 ; i<_REASON_COUNT ; i++ )
    if( p2->aRules[i] ) 
      {
	TRACE ;
	Filter_AddRules (hFilter, p2->aRules[i]) ;
	ASSERT (p1->aRules[i]!=NULL) ;
	ASSERT (p1->aRules[i]==p2->aRules[i]) ;
	p2->aRules[i] = NULL ;
      }

  Filter_Destroy (hFilterToAdd) ;
   
  return TRUE ;
}



BOOL Filter_Test (HFILTER hFilter, LPCONDITION pCond, 
		  WORD *pwReaction, WORD *pwVerbosity, WORD * pwOptions) 
{
  FILTER * p = hFilter ;
  RULE * pRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pCond!=NULL) ;
  ASSERT (pCond->nReason>=0) ;
  ASSERT (pCond->nReason<_REASON_COUNT) ;
 
  pRule = p->aRules[pCond->nReason] ;

  while( pRule )
    {
      if( Condition_Check (pRule->pCondition, pCond) )
	{
	  if( pwReaction )	*pwReaction	= pRule->nReaction ;
	  if( pwVerbosity )	*pwVerbosity	= pRule->nVerbosity ;
	  if( pwOptions )	*pwOptions	= pRule->nOptions ;
	  return TRUE ;
	}
      
      pRule = pRule->pNext ;
    }

  return FALSE ;
}


BOOL Filter_MoveRuleUp (HFILTER hFilter, RULE *pRuleToMove) 
{
  FILTER	*p = hFilter ;
  RULE		*pCurRule, *pPrevRule ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pRuleToMove!=NULL) ;
 
  pPrevRule = NULL ;
  pCurRule = p->aRules[pRuleToMove->pCondition->nReason] ;

  // is it the first rule ?
  if( pCurRule==pRuleToMove ) return TRUE ;

  // for each rule (begin)
  while( pCurRule )
    {
      // is it the next rule ?
      if( pCurRule->pNext == pRuleToMove )
	{
	  pCurRule->pNext = pRuleToMove->pNext ;
	  pRuleToMove->pNext = pCurRule ;
	  
	  if( pPrevRule )
	    pPrevRule->pNext = pRuleToMove ;
	  else
	    p->aRules[pRuleToMove->pCondition->nReason] = pRuleToMove ;

	  return TRUE ;
	}

      // iterate list
      pPrevRule = pCurRule ;
      pCurRule = pCurRule->pNext ;      
    }
  // for each rule (end)

  return FALSE ;
}


BOOL Filter_MoveRuleDown (HFILTER hFilter, RULE *pRuleToMove) 
{
  FILTER	*p = hFilter ;
  RULE		*pCurRule, *pPrevRule ;
  DWORD		nReason ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (pRuleToMove!=NULL) ;

  // is it the last rule ?
  if( ! pRuleToMove->pNext ) return TRUE ;
 
  nReason = pRuleToMove->pCondition->nReason ;
  pCurRule = p->aRules[nReason] ;

  // is it the first rule ?
  if( pCurRule==pRuleToMove ) 
    {
      p->aRules[nReason] = pRuleToMove->pNext ;
      pRuleToMove->pNext = p->aRules[nReason]->pNext ;
      p->aRules[nReason]->pNext = pRuleToMove ;
	      
      return TRUE ;
    }

  // for each rule (begin)
  while( pCurRule )
    {
      // is it this rule ?
      if( pCurRule == pRuleToMove )
	{
	  pPrevRule->pNext = pRuleToMove->pNext ;
	  pRuleToMove->pNext = pRuleToMove->pNext->pNext ;
	  pPrevRule->pNext->pNext = pRuleToMove ;
	  
	  return TRUE ;
	}

      // iterate list
      pPrevRule = pCurRule ;
      pCurRule = pCurRule->pNext ;      
    }
  // for each rule (end)

  return FALSE ;
}


BOOL	Filter_IsHookEnabled (HFILTER hFilter) 
{
  FILTER	*p = hFilter ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;

  return p->bHookEnabled ;
}


VOID	Filter_EnableHook (HFILTER hFilter, BOOL bEnable) 
{
  FILTER	*p = hFilter ;

  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  
  p->bHookEnabled = bEnable ;
}


BOOL Filter_TestPartialKey (HFILTER hFilter, LPCTSTR szPartialKey) 
{
  FILTER		*p = hFilter ;
  BOOL			bMatches = FALSE ;
  RULE			*pCurRule ;
  
  TRACE ;

  // verify params
  ASSERT (hFilter!=NULL) ;
  ASSERT (szPartialKey!=NULL) ;

  pCurRule = p->aRules[REASON_REG_SETVALUE] ;
  
  while( !bMatches && pCurRule ) 
    {
      bMatches = Condition_TestPartialKey (pCurRule->pCondition, szPartialKey) ;
      pCurRule = pCurRule->pNext ;
    }

  return bMatches ;
}
